﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Gueei.Gallery.Core.Entities;
using Gueei.Gallery.Core.Filters;
using Gueei.Gallery.Core.Services;
using Gueei.Gallery.Core.ViewModels;
using Gueei.Gallery.Web.Exceptions;
using Gueei.Gallery.Web.ViewModels;

namespace Gueei.Gallery.Web.Controllers
{
	[Authorize(Roles=Gallery.Security.GalleryRoles.EditImage)]
    public class EditImageController : Controller
    {
		private IImageService ImageService { get; set; }
		private IUserService UserService { get; set; }

		public EditImageController(IImageService imageService, IUserService userService)
		{
			ImageService = imageService;
			UserService = userService;
		}

		[HttpPost]
		public ActionResult Tags(ImageEditViewModel model)
		{
			if (ModelState.IsValidField("tagString"))
			{
				var image = UpdateProperty(model.Id, model.TagString, e => e.TagString, s => s);
				var tagString = String.Empty;
				foreach(var tag in image.Tags)
				{
					tagString += tag.Name + ",";
				}
				return SuccessResult(model, new {tagstring=tagString});
			}
			else
				return FailedResult(model);
		}

		[HttpPost]
		public ActionResult Rotate(ImageEditViewModel model, string param)
		{
			var editView = GetImageEditViewModel(model.Id);
			var angle = RotateImageFilter.RotateAngle.Rotate90;
			if (param.ToLower().Equals("rotateccw")) angle = RotateImageFilter.RotateAngle.Rotate270;
			var filter = new RotateImageFilter(angle);
			var image = ImageService.ApplyFilterAndSaveImage(editView.Id, filter);
			return SuccessResult(model, new {
				url=Url.RouteUrl("GetImage", new {id = image.Id, l = image.LastModified.Ticks})}
			);
		}

		[ValidateInput(false)]
		[HttpPost]
		public ActionResult Description(ImageEditViewModel model)
		{
			if (!ModelState.IsValidField("description")) return FailedResult(model);
			var image =	UpdateProperty(model.Id, model.Description, e => e.Description, s => s);
			return SuccessResult(model,new {description = image.Description});
		}

		[ValidateInput(false)]
		[HttpPost]
		public ActionResult Name(ImageEditViewModel model)
		{
			if (!ModelState.IsValidField("name")) return FailedResult(model);
			var image = UpdateProperty(model.Id, model.Name, e => e.Name, s => s);
			return SuccessResult(model, new {name = image.Name});
		}

		[NonAction]
		private ActionResult FailedResult(ImageEditViewModel model, object additionalData = null)
		{
			if (!Request.IsAjaxRequest())
				return RedirectToAction("Edit", "Image", model);
			else
				return new JsonResult { Data = new { success = false, data = additionalData ?? String.Empty } };
		}

		[NonAction]
		private ActionResult SuccessResult(ImageEditViewModel model, object additionalData = null)
		{
			//var json = Request.Params["json"] == null;
			if (!Request.IsAjaxRequest())
				return RedirectToAction("View", "Image", new { id = model.Id });
			else
				return new JsonResult {Data = new {success= true, data = additionalData ?? String.Empty}};
		}

		[NonAction]
		private ImageEditViewModel GetImageEditViewModel(Guid id)
		{
			Image image;
			if (id.Equals(Guid.Empty) || ((image = ImageService.GetImage(id)) == null))
				throw new ResourceNotFoundException("Image with id: " + id + " is not exists.");

			return ImageEditViewModel.CreateFromImage(image);
		}

		[NonAction]
		private Image UpdateProperty<TViewProperty, TEditProperty>(
			Guid imageId,
			TViewProperty setterValue, 
			Expression<Func<ImageEditViewModel, TEditProperty>> editExp,
			Func<TViewProperty, TEditProperty> converter)
		{
			var editModel = GetImageEditViewModel(imageId);

			editModel.GetType().InvokeMember(GetInputName(editExp), 
				System.Reflection.BindingFlags.SetProperty, null, editModel,
				new object[]{converter(setterValue)});

			return ImageService.UpdateImage(editModel.Id, editModel.TagString, 
				editModel.Name, editModel.Description, UserService.GetCurrentUser());
		}

		#region Property reflection related
		private static TProperty GetValue<TModel, TProperty>
			(TModel model, Expression<Func<TModel, TProperty>> expression) where TModel: class
		{
			if (model == null)
			{
				return default(TProperty);
			}
			Func<TModel, TProperty> func = expression.Compile();
			return func(model);
		}

		public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
		{
			if (expression.Body.NodeType == ExpressionType.Call)
			{
				var methodCallExpression = (MethodCallExpression)expression.Body;
				string name = GetInputName(methodCallExpression);
				return name.Substring(expression.Parameters[0].Name.Length + 1);

			}
			return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
		}

		private static string GetInputName(MethodCallExpression expression)
		{
			var methodCallExpression = expression.Object as MethodCallExpression;
			if (methodCallExpression != null)
			{
				return GetInputName(methodCallExpression);
			}
			return expression.Object.ToString();
		}
		#endregion
	}
}
